package cn.rentaotao.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;

/**
 * <p>Spring Security 必须配置在一个实现了 WebSecurityConfigurer 的 bean 中，或者扩展 {@link WebSecurityConfigurerAdapter}
 *
 * <p>PasswordEncoder: Spring Security 的加密接口，在加密模块中有三个实现:
 * BCryptPasswordEncoder、NoOpPasswordEncoder、StandardPasswordEncoder
 * 可以自己定义其他的加密方式
 *
 * @author rtt
 * @date 2022/5/17 10:57
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;

    @Autowired
    public WebSecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    /**
     * 用户认证
     *
     * @param auth 认证类
     * @throws Exception 异常
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 在内存中保存用户信息
        // auth.inMemoryAuthentication().withUser("rtt").password("03121994").roles("USER")
        //         .and().withUser("admin").password("03121994").roles("ADMIN", "USER");

        // 使用 jdbc 来查询用户信息
        // auth.jdbcAuthentication().dataSource(dataSource);

        /*
            基于 LDAP 认证
            LDAP: Lightweight Directory Access Protocol 轻量级目录访问协议
         */
        // auth.ldapAuthentication();

        /*
            自定义用户服务
            提供 UserDetailsService 的接口实现
         */
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
    }

    /**
     * 安全性控制配置
     *
     * @param http 安全控制类
     * @throws Exception 认证异常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*
            通过不同的路径匹配方式，指定那些路径需要认证

            access(String)              如果给定的 SpEL 表达式计算结果为 true，就允许访问
            anonymous()                 允许匿名用户访问
            authenticated()             允许认证过的用户访问
            denyAll()                   无条件拒绝访问
            fullyAuthenticated()        如果用户是完整认证的化（不是通过 Remember-me 功能认证），就允许访问
            hasAnyAuthority(String...)  如果用户具备给定权限中的某一个的话，就允许访问
            hasAnyRole(String...)       如果用户具备给定角色中的某一个的话，就允许访问
            hasAuthority(Staring)       如果用户具备给定权限的话，就允许访问
            hasIpAddress(String)        如果请求来自给定的 IP 地址的话，就允许访问
            hasRole(String)             如果用户具备给定的角色的话，就允许访问
            not()                       对其他访问方法的结果求反
            permitAll()                 无条件允许访问
            rememberMe()                如果用户是通过 Remember-me 功能认证的话，就允许访问

            requiresChannel()           将请求重定向到 https 上
         */
        // http.authorizeRequests()
        //         .antMatchers("/spittle/*").authenticated()
        //         .anyRequest().permitAll()
        //         //.and().requiresChannel().antMatchers("/spittle/*").requiresSecure().antMatchers("/").requiresInsecure()
        //         .and().csrf().disable()
        //         .formLogin()
        //         .and().httpBasic()
        //         .and().rememberMe().tokenValiditySeconds(2419200).key("spittle")
        //         .and().logout().logoutSuccessUrl("/").logoutUrl("/logout");

        http
                // csrf 设置
                .csrf().disable()

                .headers().addHeaderWriter(
                        new XFrameOptionsHeaderWriter(
                                XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)).and()
                // 登入设置
                .formLogin()
                    .defaultSuccessUrl("/index.html")
                    .loginPage("/login.html")
                    .failureUrl("/login.html?error")
                    .permitAll()
                    .and()
                // 登出设置
                .logout()
                    .logoutUrl("/logout.html")
                    .logoutSuccessUrl("/login.html?logout")
                    .permitAll()
                    .and()
                // 认证路径设置
                .authorizeRequests()
                    .antMatchers("/static/**").permitAll()
                    .antMatchers("/webjars/**").permitAll()
                    .anyRequest().authenticated()
                    .and()

                // .httpBasic().and()
                // 登入状态设置
                .rememberMe().tokenValiditySeconds(2419200).key("rtt").and();

                //.requiresChannel()

    }
}
